package ali

import (
	"context"
	"fmt"
	"github.com/golang-module/carbon/v2"
	"github.com/smartwalle/alipay/v3"
	"net/http"
	"os"
	"pay/bll/infra"
	"pay/config"
	"pay/errors"
	"pay/model"
	"time"
)

type Alipay struct {
	conf   *config.AliPayConf
	client *alipay.Client
}

func NewAlipay(conf *config.AliPayConf) (*Alipay, error) {

	if !conf.Enable {
		return &Alipay{conf: conf}, nil
	}
	// 读取公钥、私钥
	privateKeyBytes, err := os.ReadFile(conf.AppPrivateKeyPath)
	if err != nil {
		return nil, err
	}
	publicKeyBytes, err := os.ReadFile(conf.AliPayPublicKeyPath)
	if err != nil {
		return nil, err
	}
	client, err := alipay.New(conf.AppId, string(privateKeyBytes), !conf.Debug)
	if err != nil {
		return nil, err
	}
	// 加载公钥
	err = client.LoadAliPayPublicKey(string(publicKeyBytes))
	if err != nil {
		return nil, err
	}

	return &Alipay{
		conf:   conf,
		client: client,
	}, nil
}

// Prepay 支付宝支付预下单
//func (a *Alipay) Prepay(ctx context.Context, outTradeNo, desc string, fen string, orderType string) (prepayId string, expireTs int64, err error) {
//	expireTime := time.Now().Add(infra.PayExpireDuration)
//	expireTs = expireTime.Unix()
//	data := alipay.TradeAppPay{
//		Trade: alipay.Trade{
//			Subject:        desc,
//			OutTradeNo:     outTradeNo,
//			NotifyURL:      a.conf.NotifyURL,
//			TotalAmount:    fen,
//			TimeoutExpress: fmt.Sprintf("%vm", infra.PayExpireDuration.Minutes()),
//			//TimeExpire:     utils.TimeFmt(expireTime),                      // 订单超时时间
//			ProductCode:    "QUICK_MSECURITY_PAY",                          // TODO 签约自动续费的话，该值可能应为 CYCLE_PAY_AUTH，待确认
//			PassbackParams: infra.Attach{OrderType: orderType}.URLEncode(), // 额外参数
//		},
//	}
//	prepayId, err = a.client.TradeAppPay(data)
//	return
//}

// Prepay 支付宝支付预下单
func (a *Alipay) Prepay(ctx context.Context, outTradeNo, desc string, fen string, feePlanType model.FeePlanType, orderType string) (prepayId string, expireTs int64, err error) {
	expireTime := time.Now().Add(infra.PayExpireDuration)
	expireTs = expireTime.Unix()
	data := alipay.TradeAppPay{
		Trade: alipay.Trade{
			Subject:        desc,
			OutTradeNo:     outTradeNo,
			NotifyURL:      a.conf.NotifyURL,
			TotalAmount:    fen,
			TimeoutExpress: fmt.Sprintf("%vm", infra.PayExpireDuration.Minutes()),
			ProductCode:    "QUICK_MSECURITY_PAY",                          // TODO 签约自动续费的话，该值可能应为 CYCLE_PAY_AUTH，待确认
			PassbackParams: infra.Attach{OrderType: orderType}.URLEncode(), // 额外参数
		},
	}

	if feePlanType == model.FeePlanTypeYearlyRenewalPlan {
		data.ProductCode = "CYCLE_PAY_AUTH"
		data.AgreementSignParams = &alipay.SignParams{
			ProductCode:         "GENERAL_WITHHOLDING",
			PersonalProductCode: "CYCLE_PAY_AUTH_P",
			SignScene:           "INDUSTRY|GENERAL_TRAVEL",
			AccessParams: &alipay.AccessParams{
				Channel: "ALIPAYAPP",
			},
			SubMerchant: &alipay.SubMerchantParams{},
			PeriodRuleParams: &alipay.PeriodRuleParams{
				PeriodType:   "DAY",
				Period:       "7",
				ExecuteTime:  carbon.Now().AddDay().ToDateString(),
				SingleAmount: fen,
			},
			SignNotifyURL: a.conf.SignNotifyUrl,
		}
	}

	prepayId, err = a.client.TradeAppPay(data)
	return
}

// Close 系统自动去关闭订单
func (a *Alipay) Close(ctx context.Context, tradeNo string) error {
	data := alipay.TradeClose{
		TradeNo:    tradeNo,
		OperatorId: "sys",
	}
	resp, err := a.client.TradeClose(ctx, data)
	if err != nil {
		return err
	}
	if resp.IsSuccess() {
		return nil
	}
	return fmt.Errorf("AutoClose Alipay order failed:%v", resp)
}

// Refund 退款
func (a *Alipay) Refund(ctx context.Context, tradeNo string, refundAmount int, reason, refundSerial string) (*alipay.TradeRefundRsp, error) {
	data := alipay.TradeRefund{
		TradeNo:      tradeNo,
		RefundAmount: fmt.Sprintf("%.2f", float64(refundAmount)/100),
		RefundReason: reason,
		OutRequestNo: refundSerial,
	}
	return a.client.TradeRefund(ctx, data)
}

// QueryTradeOrder 查询交易订单
func (a *Alipay) QueryTradeOrder(ctx context.Context, tradeNo string) (*alipay.TradeQueryRsp, error) {
	data := alipay.TradeQuery{
		TradeNo: tradeNo,
	}
	return a.client.TradeQuery(ctx, data)
}

// ParseNotifyData 读取异步通知数据
func (a *Alipay) ParseNotifyData(req *http.Request) (*alipay.Notification, error) {
	if req == nil {
		return nil, errors.New("nil request")
	}
	err := req.ParseForm()
	if err != nil {
		return nil, err
	}
	return a.client.DecodeNotification(req.Form)
}
